package com.diy.sigmund.diycommon.spring.interceptor;

import com.diy.sigmund.diycommon.entity.constant.LogConstant;
import com.diy.sigmund.diycommon.middleware.cache.redis.RedisService;
import com.diy.sigmund.diycommon.spring.config.PortConfig;
import com.diy.sigmund.diycommon.util.json.JacksonUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * 异步处理程序拦截器
 * <br>使用异步请求处理开始后调用的回调方法扩展HandlerInterceptor 。
 * <br>当处理程序启动异步请求时， DispatcherServlet退出而不调用postHandle和afterCompletion ，因为它通常对同步请求进行处理，因为请求处理的结果（例如 ModelAndView）可能还没有准备好并且将从另一个线程同时产生。在这种情况下，将调用afterConcurrentHandlingStarted ，以允许实现在将线程释放到 Servlet 容器之前执行诸如清理线程绑定属性之类的任务。
 * <br>当异步处理完成时，请求被分派到容器进行进一步处理。在这个阶段DispatcherServlet调用preHandle 、 postHandle和afterCompletion 。为了区分初始请求和异步处理完成后的后续分派，拦截器可以检查javax.servlet.DispatcherType的javax.servlet.ServletRequest是"REQUEST"还是"ASYNC" 。
 * <br>请注意，当异步请求超时或因网络错误而完成时，可能需要执行HandlerInterceptor实现。对于这种情况，Servlet 容器不会分派，因此不会调用postHandle和afterCompletion方法。相反，拦截器可以通过WebAsyncManager上的registerCallbackInterceptor和registerDeferredResultInterceptor方法注册以跟踪异步请求。这可以在来自preHandle的每个请求上主动完成，无论异步请求处理是否开始。
 * <br>自从：3.2
 * <br>也可以看看：
 * <br>org.springframework.web.context.request.async.WebAsyncManager ，org.springframework.web.context.request.async.CallableProcessingInterceptor ， org.springframework.web.context.request.async.DeferredResultProcessingInterceptor
 * <br>作者：罗森·斯托扬切夫
 *
 * @author ylm-sigmund
 * @since 2022/7/30 23:55
 */
public class CustomAsyncHandlerInterceptor implements AsyncHandlerInterceptor {

    @Autowired
    private RedisService redisService;
    @Autowired
    private PortConfig portConfig;
    private static final Logger log = LoggerFactory.getLogger(CustomAsyncHandlerInterceptor.class);

    /**
     * <br>拦截处理程序的执行。在 HandlerMapping 确定适当的处理程序对象之后，但在 HandlerAdapter 调用处理程序之前调用。
     * <br>DispatcherServlet 处理执行链中的处理程序，该处理程序由任意数量的拦截器组成，处理程序本身位于最后。使用此方法，每个拦截器都可以决定中止执行链，通常是发送 HTTP 错误或编写自定义响应。
     * <br>注意：特殊注意事项适用于异步请求处理。有关更多详细信息，请参阅AsyncHandlerInterceptor 。
     * <br>默认实现返回true 。
     * <br>参数：
     * <br>request – 当前的 HTTP 请求
     * <br>response – 当前的 HTTP 响应
     * <br>handler - 选择执行的处理程序，用于类型和/或实例评估
     * <br>回报：
     * <br>如果执行链应该继续下一个拦截器或处理程序本身，则为true 。否则，DispatcherServlet 假定此拦截器已经处理了响应本身。
     * <br>抛出：
     * <br>Exception ——如果出现错误
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        log.info(LogConstant.SPRING_LOG, "AsyncHandlerInterceptor preHandle 开始");
        log.info("拦截处理程序的执行。在 HandlerMapping 确定适当的处理程序对象之后，但在 HandlerAdapter 调用处理程序之前调用。");
        return true;
    }


    /**
     * <br>拦截处理程序的执行。在 HandlerAdapter 实际调用处理程序之后，但在 DispatcherServlet 呈现视图之前调用。可以通过给定的 ModelAndView 向视图公开其他模型对象。
     * <br>DispatcherServlet 处理执行链中的处理程序，该处理程序由任意数量的拦截器组成，处理程序本身位于最后。使用此方法，每个拦截器都可以对执行进行后处理，以执行链的相反顺序应用。
     * <br>注意：特殊注意事项适用于异步请求处理。有关更多详细信息，请参阅AsyncHandlerInterceptor 。
     * <br>默认实现为空。
     * <br>参数：
     * <br>request – 当前的 HTTP 请求
     * <br>response – 当前的 HTTP 响应
     * <br>handler - 开始异步执行的处理程序（或HandlerMethod ），用于类型和/或实例检查
     * <br>modelAndView – 处理程序返回的ModelAndView （也可以是null ）
     * <br>抛出：
     * <br>Exception ——如果出现错误
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        log.info("AsyncHandlerInterceptor postHandle modelAndView:{}", JacksonUtil.toJson(modelAndView));
        log.info("拦截处理程序的执行。在 HandlerAdapter 实际调用处理程序之后，但在 DispatcherServlet 呈现视图之前调用。可以通过给定的 ModelAndView 向视图公开其他模型对象。");
    }

    /**
     * <br>请求处理完成后的回调，即渲染视图后。将在处理程序执行的任何结果上调用，从而允许适当的资源清理。
     * <br>注意：仅当此拦截器的preHandle方法成功完成并返回true时才会调用！
     * <br>与postHandle方法一样，该方法将在链中的每个拦截器上以相反的顺序调用，因此第一个拦截器将是最后一个被调用的拦截器。
     * <br>注意：特殊注意事项适用于异步请求处理。有关更多详细信息，请参阅AsyncHandlerInterceptor 。
     * <br>默认实现为空。
     * <br>参数：
     * <br>request – 当前的 HTTP 请求
     * <br>response – 当前的 HTTP 响应
     * <br>handler - 开始异步执行的处理程序（或HandlerMethod ），用于类型和/或实例检查
     * <br>ex – 处理程序执行时抛出的任何异常，如果有的话；这不包括已通过异常解析器处理的异常
     * <br>抛出：
     * <br>Exception ——如果出现错误
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
            Object handler, Exception ex) throws Exception {
        log.info("AsyncHandlerInterceptor afterCompletion");
        log.info("请求处理完成后的回调，即渲染视图后。将在处理程序执行的任何结果上调用，从而允许适当的资源清理。 注意：仅当此拦截器的preHandle方法成功完成并返回true时才会调用！");
    }

    /**
     * <br>当处理程序同时执行时调用而不是postHandle和afterCompletion 。
     * <br>实现可以使用提供的请求和响应，但应避免以与处理程序的并发执行冲突的方式修改它们。此方法的典型用途是清理线程局部变量。
     * <br>参数：
     * <br>request – 当前请求
     * <br>response – 当前响应
     * <br>handler - 开始异步执行的处理程序（或HandlerMethod ），用于类型和/或实例检查
     * <br>抛出：
     * <br>Exception ——如果出现错误
     */
    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // if (Objects.nonNull(ContextHolder.getInstance().getCustomUser())) {
        //     ContextHolder.getInstance().remove();
        //     log.info("AsyncHandlerInterceptor 清除主线程的用户上下文 成功");
        // }
        log.info("当处理程序同时执行时调用而不是postHandle和afterCompletion 。 实现可以使用提供的请求和响应，但应避免以与处理程序的并发执行冲突的方式修改它们。此方法的典型用途是清理线程局部变量。");
        log.info(LogConstant.SPRING_LOG, "AsyncHandlerInterceptor afterConcurrentHandlingStarted 结束");
    }
}
